home *** CD-ROM | disk | FTP | other *** search
- page ,132
- TITLE GPFILT
- subttl General Purpose Filter Template
- ;
- ; GPFILT.ASM
- ; This file contains a template for a general-purpose assembly language
- ; filter program.
- ;
- ; Fill in the blanks for what you wish to do. The program is set up to
- ; accept a command line in the form:
- ; COMMAND [{-|/}options] [infile [outfile]]
- ;
- ; If infile is not specified, stdin is used.
- ; If outfile is not specified, stdout is used.
- ;
- ; To compile and link:
- ; MASM GPFILT ;
- ; LINK GPFILT ;
- ; EXE2BIN GPFILT GPFILT.COM
- ;
- ; Standard routines supplied in the general shell are:
- ;
- ; get_arg - returns the address of the next command line argument in
- ; DX. Since this is a .COM file, the routine assumes DS will
- ; be the same as the command line segment.
- ; The routine will return with Carry set when it reaches the end
- ; of the command line.
- ;
- ; err_msg - displays an ASCIIZ string on the STDERR device. Call with the
- ; address of the string in ES:DX.
- ;
- ; do_usage- displays the usage message on the STDERR device and exits
- ; with an error condition (errorlevel 1). This routine will
- ; never return.
- ;
- ; getch - returns the next character from the input stream in AL.
- ; It will return with carry set if an error occurs during read.
- ; It will return with the ZF set at end of file.
- ;
- ; putch - writes a character from AL to the output stream. Returns with
- ; carry set if a write error occurs.
- ;
- cseg segment
- assume cs:cseg, ds:cseg, es:cseg, ss:cseg
-
- org 0100h ;for .COM files
-
- start: jmp main ;jump around data area
-
- ;
- ; Equates and global data area.
- ;
- ; The following equates and data areas are required by the general filter
- ; routines. User data area follows.
- ;
- STDIN equ 0
- STDOUT equ 1
- STDERR equ 2
- STDPRN equ 3
- cr equ 0dh
- lf equ 0ah
- space equ 32
- tab equ 9
-
- infile dw STDIN ;default input file is stdin
- outfile dw STDOUT ;default output file is stdout
- errfile dw STDERR ;default error file is stderr
- prnfile dw STDPRN ;default print file is stdprn
- cmd_ptr dw 0081h ;address of first byte of command tail
- PSP_ENV equ 002ch ;The segment address of the environment
- ;block is stored here.
-
- infile_err db cr, lf, 'Error opening input file', 0
- outfile_err db cr, lf, 'Error opening output file', 0
- aborted db 07, cr, lf, 'Program aborted', 0
- usage db cr, lf, 'Usage: ', 0
- crlf db cr, lf, 0
-
- ;************************************************************************
- ;* *
- ;* Buffer sizes for input and output files. The buffers need not be *
- ;* the same size. For example, a program that removes tabs from a text *
- ;* file will output more characters than it reads. Therefore, the *
- ;* output buffer should be slightly larger than the input buffer. In *
- ;* general, the larger the buffer, the faster the program will run. *
- ;* *
- ;* The only restriction here is that the combined size of the buffers *
- ;* plus the program code and data size cannot exceed 64K. *
- ;* *
- ;* The easiest way to determine maximum available buffer memory is to *
- ;* assemble the program with minimum buffer sizes and examine the value *
- ;* of the endcode variable at the end of the program. Subtracting this *
- ;* value from 65,536 will give you the total buffer memory available. *
- ;* *
- ;************************************************************************
- ;
- INNBUF_SIZE equ 31 ;size of input buffer (in K)
- OUTBUF_SIZE equ 31 ;size of output buffer (in K)
-
- ;
- ;************************************************************************
- ;* *
- ;* Data definitions for input and output buffers. DO NOT modify these *
- ;* definitions unless you know exactly what it is you're doing! *
- ;* *
- ;************************************************************************
- ;
- ; Input buffer
- ibfsz equ 1024*INNBUF_SIZE ;input buffer size in bytes
- inbuf equ endcode ;input buffer
- ibfend equ inbuf + ibfsz ;end of input buffer
- ;
- ; ibfptr is initialized to point past end of input buffer so that the first
- ; call to getch will result in a read from the file.
- ;
- ibfptr dw inbuf+ibfsz
-
- ; output buffer
- obfsz equ 1024*OUTBUF_SIZE ;output buffer size in bytes
- outbuf equ ibfend ;output buffer
- obfend equ outbuf + obfsz ;end of output buffer
- obfptr dw outbuf ;start at beginning of buffer
-
- ;************************************************************************
- ;* *
- ;* USER DATA AREA *
- ;* *
- ;* Insert any data declarations specific to your program here. *
- ;* *
- ;* NOTE: The prog_name, use_msg, and use_msg1 variables MUST be *
- ;* defined. *
- ;* *
- ;************************************************************************
- ;
- ; This is the program name. Under DOS 3.x, this is not used because we
- ; can get the program name from the environment. Prior to 3.0, this
- ; information is not supplied by the OS.
- ;
- prog_name db 'GPFILT', 0
- ;
- ; This is the usage message. The first two lines are required.
- ; The first line is the programs title line.
- ; Make sure to include the 0 at the end of the first line!!
- ; The second line shows the syntax of the program.
- ; Following lines (which are optional), are discussion of options, features,
- ; etc...
- ; The message MUST be terminated by a 0.
- ;
- use_msg db ' - General Purpose FILTer program.', cr, lf, 0
- use_msg1 label byte
- db '[{-|/}options] [infile [outfile]]', cr, lf
- db cr, lf
- db 'If infile is not specified, STDIN is used', cr, lf
- db 'If outfile is not specified, STDOUT is used', cr, lf
- db 0
- ;
- ;************************************************************************
- ;* *
- ;* The main routine parses the command line arguments, opens files, and *
- ;* does other initialization tasks before calling the filter procedure *
- ;* to do the actual work. *
- ;* For a large number of filter programs, this routine will not need to *
- ;* be modified. Options are parsed in the get_options proc., and the *
- ;* filter proc. does all of the 'filter' work. *
- ;* *
- ;************************************************************************
- ;
- main: cld
- call get_options ;process options
- jc gofilter ;carry indicates end of arg list
- mov ah,3dh ;open file
- mov al,0 ;read access
- int 21h ;open the file
- mov word ptr ds:[infile], ax ;save file handle
- jnc main1 ;carry clear indicates success
- mov dx,offset infile_err
- jmp short err_exit
- main1: call get_arg ;get cmd line arg in DX
- jc gofilter ;carry indicates end of arg list
- mov ah,3ch ;create file
- mov cx,0 ;normal file
- int 21h ;open the file
- mov word ptr ds:[outfile],ax ;save file handle
- jnc gofilter ;carry clear indicates success
- mov dx,offset outfile_err
- jmp short err_exit
- gofilter:
- call filter ;do the work
- jc err_exit ;exit immediately on error
- mov ah,3eh
- mov bx,word ptr [infile]
- int 21h ;close input file
- mov ah,3eh
- mov bx,word ptr [outfile]
- int 21h ;close output file
- mov ax,4c00h
- int 21h ;exit with no error
- err_exit:
- call err_msg ;output error message
- mov dx,offset aborted
- call err_msg
- mov ax,4c01h
- int 21h ;and exit with error
- ;
- ;************************************************************************
- ;* *
- ;* get_options processes any command line options. Options are *
- ;* preceeded by either - or /. There is a lot of flexibility here. *
- ;* Options can be specified separately, or as a group. For example, *
- ;* the command "GPFILT -x -y -z" is equivalent to "GPFILT -xyz". *
- ;* *
- ;* This routine MUST return the address of the next argument in DX or *
- ;* carry flag set if there are no more options. In other words, return *
- ;* what was returned by the last call to get_arg. *
- ;* *
- ;************************************************************************
- ;
- get_options proc
- call get_arg ;get command line arg
- jnc opt1
- ; If at least one argument is required, use this line
- ; call do_usage ;displays usage msg and exits
- ; If there are no required args, use this line
- ret ;if no args, just return
- opt1: mov di, dx
- mov al,byte ptr ds:[di]
- cmp al,'-' ;if first character of arg is '-'
- jz opt_parse
- cmp al,'/' ;or '/', then get options
- jz opt_parse
- ret ;otherwise exit
- opt_parse:
- inc di
- mov al,byte ptr ds:[di]
- or al,al ;if end of options string
- jz nxt_opt ;get cmd. line arg
- cmp al,'?' ;question means show usage info
- jz do_usage
- ;
- ;************************************************************************
- ;* *
- ;* Code for processing other options goes here. The current option *
- ;* character is in AL, and the remainder of the option string is pointed*
- ;* to by DS:DI. *
- ;* *
- ;************************************************************************
- ;
- jmp short opt_parse
-
- nxt_opt:
- call get_arg ;get next command line arg
- jnc opt1 ;if carry
- vld_args: ;then validate arguments
- ;
- ;************************************************************************
- ;* *
- ;* Validate arguments. If some options are mutually exclusive/dependent*
- ;* use this area to validate them. Whatever the case, if you must *
- ;* abort the program, call the do_usage procedure to display the usage *
- ;* message and exit the program. *
- ;* *
- ;************************************************************************
- ;
- ret ; no more options
- ;
- ;************************************************************************
- ;* *
- ;* Filter does all the work. Modify this routine to do what it is you *
- ;* need done. *
- ;* *
- ;************************************************************************
- ;
- filter proc
- call getch ;get a character from input into AL
- jbe filt_done ;exit on error or EOF
- and al, 7fh ;strip the high bit
- call putch ;and output it
- jc filt_ret ;exit on error
- jmp short filter
- filt_done:
- jc filt_ret ;carry set is error
- call write_buffer ;output what remains of the buffer
- filt_ret:
- ret
- filter endp
- ;
- ;************************************************************************
- ;* *
- ;* Put any program-specific routines here *
- ;* *
- ;************************************************************************
-
- ;
- ;************************************************************************
- ;* *
- ;* For most programs, nothing beyond here should require modification. *
- ;* The routines that follow are standard routines used by almost every *
- ;* filter program. *
- ;* *
- ;************************************************************************
- ;
- ;************************************************************************
- ;* *
- ;* This routine outputs the usage message to the STDERR device and *
- ;* aborts the program with an error code. A little processing is done *
- ;* here to get the program name and format the output. *
- ;* *
- ;************************************************************************
- ;
- do_usage:
- mov dx, offset crlf
- call err_msg ;output newline
- mov ah,30h ;get DOS version number
- int 21h
- sub al,3 ;check for version 3.x
- jc lt3 ;if carry, earlier than 3.0
- ;
- ; For DOS 3.0 and later the full pathname of the file used to load this
- ; program is stored at the end of the environment block. We first scan
- ; all of the environment strings in order to find the end of the env, then
- ; scan the load pathname looking for the file name.
- ;
- push es
- mov ax, word ptr ds:[PSP_ENV]
- mov es, ax ;ES is environment segment address
- mov di, 0
- mov cx, 0ffffh ;this ought to be enuf
- xor ax, ax
- getvar: scasb ;get char
- jz end_env ;end of environment
- gv1: repnz scasb ;look for end of variable
- jmp short getvar ;and loop 'till end of environment
- end_env:
- inc di
- inc di ;bump past word count
- ;
- ; ES:DI is now pointing to the beginning of the pathname used to load the
- ; program. We will now scan the filename looking for the last path specifier
- ; and use THAT address to output the program name. The program name is
- ; output WITHOUT the extension.
- ;
- mov dx, di
- fnloop: mov al, byte ptr es:[di]
- or al, al ;if end of name
- jz do30 ;then output it
- inc di
- cmp al, '\' ;if path specifier
- jz updp ;then update path pointer
- cmp al, '.' ;if '.'
- jnz fnloop
- mov byte ptr es:[di-1], 0 ;then place a 0 so we don't get ext
- jmp short fnloop ; when outputting prog name
- updp: mov dx, di ;store
- jmp short fnloop
- ;
- ; ES:DX now points to the filename of the program loaded (without extension).
- ; Output the program name and then go on with rest of usage message.
- ;
- do30: call err_msg ;output program name
- pop es ;restore
- jmp short gopt3
- ;
- ; We arrive here if the current DOS version is earlier than 3.0. Since the
- ; loaded program name is not available from the OS, we'll output the name
- ; entered in the 'prog_name' field above.
- ;
- lt3: mov dx, offset prog_name
- call err_msg ;output the program name
- ;
- ; After outputting program name, we arrive here to output the rest of the
- ; usage message. This code assumes that the usage message has been
- ; written as specified in the data area.
- ;
- gopt3: mov dx, offset use_msg
- call err_msg ;output the message
- mov dx, offset usage
- call err_msg
- mov dx, offset use_msg1
- call err_msg
- mov ax,4c01h
- int 21h ;and exit with error
- get_options endp
-
- ;
- ;************************************************************************
- ;* *
- ;* Output a message (ASCIIZ string) to the standard error device. *
- ;* Call with address of error message in ES:DX. *
- ;* *
- ;************************************************************************
- ;
- err_msg proc
- cld
- mov di,dx ;string address in di
- mov cx,0ffffh
- xor ax,ax
- repnz scasb ;find end of string
- xor cx,0ffffh
- dec cx ;CX is string length
- push ds
- mov ax,es
- mov ds,ax ;DS is segment address
- mov ah,40h
- mov bx,word ptr cs:[errfile]
- int 21h ;output message
- pop ds
- ret
- err_msg endp
-
- ;
- ;************************************************************************
- ;* *
- ;* getch returns the next character from the file in AL. *
- ;* Returns carry = 1 on error *
- ;* ZF = 1 on EOF *
- ;* Upon exit, if either Carry or ZF is set, the contents of AL is *
- ;* undefined. *
- ;* *
- ;************************************************************************
- ;
- ; Local variables used by the getch proc.
- eof db 0 ;set to 1 when EOF reached in read
- last_ch dw ibfend ;pointer to last char in buffer
-
- getch proc
- mov si,word ptr ds:[ibfptr] ;get input buffer pointer
- cmp si,word ptr ds:[last_ch];if not at end of buffer
- jz getch_eob
- getch1: lodsb ;character in AL
- mov word ptr ds:[ibfptr],si ;save buffer pointer
- or ah,1 ;will clear Z flag
- ret ;and done
-
- getch_eob: ;end of buffer processing
- cmp byte ptr ds:[eof], 1 ;end of file?
- jnz getch_read ;nope, read file into buffer
- getch_eof:
- xor ax, ax ;set Z to indicate EOF
- ret ;and return
-
- getch_read: ; Read the next buffer full from the file.
- mov ah,3fh ;read file function
- mov bx,word ptr ds:[infile] ;input file handle
- mov cx,ibfsz ;#characters to read
- mov dx,offset inbuf ;read into here
- int 21h ;DOS'll do it for us
- jc read_err ;Carry means error
- or ax,ax ;If AX is zero,
- jz getch_eof ;we've reached end-of-file
- add ax,offset inbuf
- mov word ptr ds:[last_ch],ax;and save it
- mov si,offset inbuf
- jmp short getch1 ;and finish processing character
-
- read_err: ;return with error and...
- mov dx,offset read_err_msg ; DX pointing to error message string
- ret
- read_err_msg db 'Read error', cr, lf, 0
- getch endp
-
- ;
- ;************************************************************************
- ;* *
- ;* putch writes the character passed in AL to the output file. *
- ;* Returns carry set on error. The character in AL is retained. *
- ;* *
- ;************************************************************************
- ;
- putch proc
- mov di,word ptr ds:[obfptr] ;get output buffer pointer
- stosb ;save the character
- mov word ptr ds:[obfptr],di ;and update buffer pointer
- cmp di,offset obfend ;if buffer pointer == buff end
- clc
- jnz putch_ret
- push ax
- call write_buffer ;then we've got to write the buffer
- pop ax
- putch_ret:
- ret
- putch endp
-
- ;
- ;************************************************************************
- ;* *
- ;* write_buffer writes the output buffer to the output file. *
- ;* This routine should not be called except by the putch proc. and at *
- ;* the end of all processing (as demonstrated in the filter proc). *
- ;* *
- ;************************************************************************
- ;
- write_buffer proc ;write buffer to output file
- mov ah, 40h ;write to file function
- mov bx, word ptr ds:[outfile];output file handle
- mov cx, word ptr ds:[obfptr]
- sub cx, offset outbuf ;compute #bytes to write
- mov dx, offset outbuf ;from this buffer
- int 21h ;DOS'll do it
- jc write_err ;carry is error
- or ax,ax ;return value of zero
- jz putch_full ;indicates disk full
- mov word ptr ds:[obfptr],offset outbuf
- clc
- ret
-
- putch_full: ;disk is full
- mov dx,offset disk_full
- stc ;exit with error
- ret
-
- write_err: ;error occured during write
- mov dx,offset write_err_msg
- stc ;return with error
- ret
- write_err_msg db 'Write error', cr, lf, 0
- disk_full db 'Disk full', cr, lf, 0
-
- write_buffer endp
-
- ;
- ;************************************************************************
- ;* *
- ;* get_arg - Returns the address of the next command line argument in *
- ;* DX. The argument is in the form of an ASCIIZ string. *
- ;* Returns Carry = 1 if no more command line arguments. *
- ;* Upon exit, if Carry is set, the contents of DX is undefined. *
- ;* *
- ;************************************************************************
- ;
- get_arg proc
- mov si,word ptr [cmd_ptr]
- skip_space: ;scan over leading spaces and commas
- lodsb
- cmp al,0 ;if we get a null
- jz sk0
- cmp al,cr ;or a CR,
- jnz sk1
- sk0: stc ;set carry to indicate failure
- ret ;and exit
- sk1: cmp al,space
- jz skip_space ;loop until no more spaces
- cmp al,','
- jz skip_space ;or commas
- cmp al,tab
- jz skip_space ;or tabs
-
- mov dx,si ;start of argument
- dec dx
- get_arg1:
- lodsb ;get next character
- cmp al,cr ;argument seperators are CR,
- jz get_arg2
- cmp al,space ;space,
- jz get_arg2
- cmp al,',' ;comma,
- jz get_arg2
- cmp al,tab ;and tab
- jnz get_arg1
-
- get_arg2:
- mov byte ptr ds:[si-1], 0 ;delimit argument with 0
- cmp al, cr ;if char is CR then we've reached
- jnz ga2 ; the end of the argument list
- dec si
- ga2: mov word ptr ds:[cmd_ptr], si ;save for next time 'round
- ret ;and return
- get_arg endp
-
- endcode equ $
-
- cseg ends
- end start